/*    file: WinVideoWindow.cpp
 *    desc:
 * 
 * created: 2014-01-21 14:12:16
 *  author: zhengchuanjiang
 * company: 
 */

//#include "stdafx.h"
#include "WinVideoWindow.h"
#include "Path.h"
#include "FrameToBitmap.h"
#include "TStringUtil.h"
#include <assert.h>

#include "AppProperties.h"
#include "MediaProp.h"
#include "CLog.h"


//////////////////////////////////////////////////////////////////////////
static const UINT_PTR TIMER_CONTROL = 1;
static const UINT ELAPSE_CONTROL = 1000 * 4;

static const UINT_PTR TIMER_TRACK = 2;
static const UINT ELAPSE_TRACK = 500;


//////////////////////////////////////////////////////////////////////////
namespace av
{

WinVideoWindow::WinVideoWindow():
	m_hWnd(),
    m_ratioX(),
    m_ratioY(),
    m_enableControl(),
    m_fullscreen(),
    m_rcLast(),
    m_state(STATE_STOPPED),
	m_superWindowProc()
{
    std::string workDir = comn::Path::getWorkDir();
    std::string filepath = comn::Path::join(workDir, "logo.png");
    setBackImage(filepath.c_str());
}

WinVideoWindow::~WinVideoWindow()
{
	close();
}

int WinVideoWindow::open(void* hwnd)
{
    m_hWnd = (HWND)hwnd;
    subclassWindow(m_hWnd);
    
    return 0;
}

void WinVideoWindow::close()
{
    unsubclassWindow(m_hWnd);

    m_hWnd = NULL;

    clearBuffer();
}

bool WinVideoWindow::isOpen()
{
    return (TRUE == ::IsWindow(getHwnd()));
}

void WinVideoWindow::setVideoWnd(void* hwnd)
{
    unsubclassWindow(m_hWnd);

    m_hWnd = (HWND)hwnd;

    subclassWindow(m_hWnd);

    SetParent(getHwnd(), m_hWnd);

    RECT rc;
    ::GetClientRect(m_hWnd, &rc);
    MoveWindow(rc, TRUE);
}


void WinVideoWindow::setState(StreamState state)
{
    comn::AutoCritSec lock(m_cs);

    m_state = state;

    ::PostMessage(m_hWnd, WM_SET_STATE, state, 0);
}

int WinVideoWindow::draw(AVFramePtr frame)
{
    if (m_state == STATE_STOPPED)
    {
        return 0;
    }

	{
		comn::AutoCritSec lock(m_cs);
		m_backFrame = frame;
	}
    
    Invalidate(FALSE);

    return 0;
}


bool WinVideoWindow::isFullscreen()
{
    return m_fullscreen;
}

int WinVideoWindow::setFullscreen(bool full)
{
    if (full == m_fullscreen)
    {
        return 0;
    }

    putFullScreen(full);

    m_fullscreen = full;

    return 0;
}

int WinVideoWindow::setRatio(int width, int height)
{
    {
        comn::AutoCritSec lock(m_cs);
        m_ratioX = width;
        m_ratioY = height;
    }

    Invalidate(TRUE);

    return 0;
}

void WinVideoWindow::getRatio(int& width, int& height)
{
    width = m_ratioX;
    height = m_ratioY;
}

int WinVideoWindow::setBackImage(const char* filepath)
{
    int rc = ENOENT;
	return rc;
}

void WinVideoWindow::flush()
{
    {
        comn::AutoCritSec lock(m_cs);
        m_backFrame.reset();
        m_curFrame.reset();
    }

    m_presenter.reset();
}


void WinVideoWindow::drawBackImage(HDC hdc)
{
    RECT rc;
    ::GetClientRect(m_hWnd, &rc);
    ::FillRect(hdc, &rc, (HBRUSH)GetStockObject(BLACK_BRUSH));
}

void WinVideoWindow::drawBackground(HDC hdc)
{
    RECT rc;
    ::GetClientRect(m_hWnd, &rc);

    HBRUSH hBrush = (HBRUSH)GetStockObject(BLACK_BRUSH);

    RECT rcVideo = getVideoRect();
    if (IsRectEmpty(&rcVideo))
    {
        ::FillRect(hdc, &rc, hBrush);
    }
    else
    {
        RECT rcTop(rc);
        rcTop.bottom = rcVideo.top;
        ::FillRect(hdc, &rcTop, hBrush);

        RECT rcLeft(rc);
        rcLeft.right = rcVideo.left;
        ::FillRect(hdc, &rcLeft, hBrush);

        RECT rcRight(rc);
        rcRight.left = rcVideo.right;
        ::FillRect(hdc, &rcRight, hBrush);

        RECT rcBottom(rc);
        rcBottom.top = rcVideo.bottom;
        ::FillRect(hdc, &rcBottom, hBrush);
    }
}

void WinVideoWindow::drawFrame()
{
	fetchFrame();

	comn::AutoCritSec lock(m_cs);
	if (m_curFrame)
	{
		m_presenter.draw(m_hWnd, m_curFrame.get(), m_ratioX, m_ratioY);
	}
	else
	{
		drawBackground();
	}
}

void WinVideoWindow::drawBackground()
{
	HDC hdc = GetWindowDC(getHwnd());
	drawBackImage(hdc);
	ReleaseDC(getHwnd(), hdc);
}

static BOOL screenToClient(HWND hwnd, RECT& rc)
{
    POINT ptTop = {rc.left, rc.top};
    POINT ptBottom = {rc.right, rc.bottom};
    BOOL done = ::ScreenToClient(hwnd, &ptTop);
    ::ScreenToClient(hwnd, &ptBottom);
    rc.left = ptTop.x;
    rc.top = ptTop.y;
    rc.right = ptBottom.x;
    rc.bottom = ptBottom.y;
    return done;
}

static BOOL moveWindow(HWND hwnd, const RECT& rc, BOOL bRepaint)
{
    return MoveWindow(hwnd, rc.left, rc.top,
        rc.right - rc.left,
        rc.bottom - rc.top, bRepaint);
}

void WinVideoWindow::putFullScreen(bool full)
{
    if (full)
    {
		m_parentWnd = GetParent(m_hWnd);

        ::GetWindowRect(m_hWnd, &m_rcLast);
        screenToClient(m_hWnd, m_rcLast);

        RECT rc;
        ::GetWindowRect(m_hWnd, &rc);

        HMONITOR  hMonitor = MonitorFromRect(&rc, MONITOR_DEFAULTTONEAREST);
        MONITORINFO mi;
        mi.cbSize = sizeof(mi);
        GetMonitorInfo(hMonitor, &mi);
        RECT rcFull = mi.rcMonitor;
        //TRACE("rcFull. %d,%d,%d,%d\n", rcFull.left, rcFull.top, rcFull.right, rcFull.bottom);

        HWND hDesktop = ::GetDesktopWindow();
        ::SetParent(m_hWnd, hDesktop);

        //rcFull.right = 400;
        //rcFull.bottom = 400;

        moveWindow(m_hWnd, rcFull, TRUE);
        ::SetWindowPos(m_hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
    }
    else
    {
        ::SetParent(m_hWnd, m_parentWnd);

        if (!::IsRectEmpty(&m_rcLast))
        {
            moveWindow(m_hWnd, m_rcLast, TRUE);
        }
    }
}

void WinVideoWindow::onSetState(int state)
{
    if (state == STATE_PLAYING)
    {
        drawFrame();
    }
    else if (state == STATE_STOPPED)
    {
        m_presenter.reset();

        Invalidate(TRUE);

        clearBuffer();
    }
}

void WinVideoWindow::clearBuffer()
{
    comn::AutoCritSec lock(m_cs);
    m_backFrame.reset();
}


bool WinVideoWindow::getVideoSize(int& width, int& height)
{
    bool found = false;
    comn::AutoCritSec lock(m_cs);
    if (m_curFrame)
    {
        width = m_curFrame->width;
        height = m_curFrame->height;
        found = true;
    }
    return found;
}

RECT WinVideoWindow::getVideoRect()
{
	RECT rc = {0};
    int picWidth = 0;
    int picHeight = 0;
    if (!getVideoSize(picWidth, picHeight))
    {
        return rc;
    }

    GetClientRect(m_hWnd, &rc);
	int width = rc.right - rc.left;
	int height = rc.bottom - rc.top;
    return HwndPresenter::getDestRect(picWidth, picHeight, 
        width, height, m_ratioX, m_ratioY);
}

LRESULT WinVideoWindow::OnPaint(WPARAM wParam, LPARAM lParam)
{
	LRESULT ret = 0;
	// ::CallWindowProc(m_superWindowProc, m_hWnd, WM_PAINT, wParam, lParam);

    if (!::IsWindow(m_hWnd))
    {
        return ret;
    }

	PAINTSTRUCT ps;
	HDC hdc = BeginPaint(m_hWnd, &ps);

    if (m_state == STATE_PLAYING)
    {
        drawFrame();
    }
    else if (m_state == STATE_PAUSED)
    {
        drawFrame();
    }
    else
    {
        drawBackImage(hdc);
    }

	EndPaint(m_hWnd, &ps);

    return ret;
}

LRESULT WinVideoWindow::OnEraseBkgnd(WPARAM wParam, LPARAM lParam)
{
	HDC hdc = (HDC)wParam;
    //AppLog::debug("OnEraseBkgnd\n");
    drawBackground(hdc);

    return TRUE;
}

LRESULT WinVideoWindow::OnLButtonDblClk(WPARAM wParam, LPARAM lParam)
{
	//if (AppProperties::getProperties().getBool(MEDIA_VIDEO_PROP_ENABLE_FULLSCREEN, false))
	{
		setFullscreen(!m_fullscreen);
		return 0;
	}

	return ::CallWindowProc(m_superWindowProc, m_hWnd, WM_LBUTTONDBLCLK, wParam, lParam);
}

void WinVideoWindow::fetchFrame()
{
    comn::AutoCritSec lock(m_cs);
    if (m_backFrame)
    {
		if (m_curFrame)
		{
			if (m_curFrame->width != m_backFrame->width ||
				m_curFrame->height != m_backFrame->height)
			{
				Invalidate(TRUE);
			}
		}

        m_curFrame = m_backFrame;
        m_backFrame.reset();
    }
}

HWND WinVideoWindow::getParentWnd()
{
	return m_hWnd;
}

void WinVideoWindow::subclassWindow(HWND hwnd)
{
	if (hwnd == NULL)
	{
		return;
	}

	::SetWindowLong(hwnd, GWL_USERDATA, (LONG)(void*)this);

	m_superWindowProc = (WNDPROC)::SetWindowLong(hwnd, GWL_WNDPROC, (LONG)MyWndProc);

	::InvalidateRect(hwnd, NULL, TRUE);
}

void WinVideoWindow::unsubclassWindow(HWND hwnd)
{
	if (hwnd == NULL)
	{
		return;
	}

	::SetWindowLong(hwnd, GWL_WNDPROC, (LONG)m_superWindowProc);
	m_superWindowProc = NULL;
}

LRESULT CALLBACK WinVideoWindow::MyWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	//CLog::debug("WinVideoWindow::MyWndProc. message:%04x\n", message);

	WinVideoWindow *pObject = (WinVideoWindow*)::GetWindowLong(hWnd, GWL_USERDATA);
	return pObject->parentWndProc(hWnd, message, wParam, lParam);
}

LRESULT WinVideoWindow::OnSize(WPARAM wParam, LPARAM lParam)
{
	//
	return 0;
}

HWND WinVideoWindow::getHwnd()
{
	return m_hWnd;
}

void WinVideoWindow::Invalidate(BOOL bErase)
{
	::InvalidateRect(getHwnd(), NULL, bErase);
}

void WinVideoWindow::MoveWindow(const RECT& rc, BOOL repaint)
{
	::MoveWindow(getHwnd(), rc.left, rc.top,
		rc.right - rc.left,
		rc.bottom - rc.top, repaint);
}

LRESULT WinVideoWindow::parentWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_SIZE:
	{
		return OnSize(wParam, lParam);
	}
	break;
	case WM_DESTROY:
	{
		return ::CallWindowProc(m_superWindowProc, hWnd, message, wParam, lParam);
	}
	break;
	case WM_PAINT:
	{
		return OnPaint(wParam, lParam);
	}
	break;
	case WM_ERASEBKGND:
	{
		return OnEraseBkgnd(wParam, lParam);
	}
	case WM_SET_STATE:
	{
		onSetState(wParam);
		break;
	}
	case WM_LBUTTONDBLCLK:
	{
		return OnLButtonDblClk(wParam, lParam);
		break;
	}
	default:
		return ::CallWindowProc(m_superWindowProc, hWnd, message, wParam, lParam);
	}
	return 0;
}


}
